<?php

namespace App\Http\Controllers;

use App\Imports\DormMeterRecordImport;
use App\Models\Building;
use App\Models\Dorm;
use App\Models\DormMeter;
use App\Models\DormMeterRecord;
use App\Models\MeterType;
use App\Models\Period;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Laravel\Passport\Client;
use Maatwebsite\Excel\Facades\Excel;
use Prettus\Validator\Contracts\ValidatorInterface;
use App\Http\Requests\DormMeterRecordCreateRequest;
use App\Http\Requests\DormMeterRecordUpdateRequest;
use App\Repositories\DormMeterRecordRepository;
use App\Validators\DormMeterRecordValidator;
use App\Presenters\DormMeterRecordPresenter;
use Illuminate\Support\Str;

/**
 * Class DormMeterRecordsController.
 *
 * @package namespace App\Http\Controllers;
 */
class DormMeterRecordsController extends Controller
{
    /**
     * @var DormMeterRecordRepository
     */
    protected $repository;

    /**
     * @var DormMeterRecordValidator
     */
    protected $validator;

    /**
     * DormMeterRecordsController constructor.
     *
     * @param DormMeterRecordRepository $repository
     * @param DormMeterRecordValidator $validator
     */
    public function __construct(DormMeterRecordRepository $repository, DormMeterRecordValidator $validator)
    {
        $this->repository = $repository;
        $this->validator = $validator;
    }

    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index(Request $request)
    {
        $this->repository->pushCriteria(app('Prettus\Repository\Criteria\RequestCriteria'));
        $this->repository->setPresenter(DormMeterRecordPresenter::class);
        if (\Route::currentRouteName() == 'api.dorm-meter-records.index') {
            //whereNotNull('client_id') 可避免当遇到有换表时，把换表生成的记录也同步到已抄中

            if ($request->has('company_id')) {
                $dormMeterRecords = $this->repository->whereNotNull('client_id')->where('company_id', $request->company_id)->get();
            } else {
                $dormMeterRecords = $this->repository->whereNotNull('client_id')->get();
            }
        } else {
            if (\request()->has('export')) {
                $dormMeterRecords = $this->repository->all();
            } else {
                $dormMeterRecords = $this->repository->paginate(request()->get('limit', 20));
            }
        }
        if (request()->wantsJson()) {
            return response()->json([
                'data' => $dormMeterRecords,
            ]);
        }
        \request()->flash();
        $periods = Period::orderBy('name', 'desc')->limit(24)->pluck('name', 'id');
        $meterTypes = MeterType::all()->pluck('name', 'id');
        $buildings = Building::all()->pluck('name', 'id');
        return view('dormMeterRecords.index', compact('dormMeterRecords', 'periods', 'meterTypes', 'buildings'));
    }

    public function create()
    {
        $dorms = Dorm::with('activeWaterMeter', 'activeElectricMeter')->has('activeWaterMeter')->orHas('activeElectricMeter')->get();
        $meterTypes = MeterType::all();
        $periods = Period::where('status', '<', 2)->get();
        return view('dormMeterRecords.create', compact('dorms', 'meterTypes', 'periods'));
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  DormMeterRecordCreateRequest $request
     *
     * @return \Illuminate\Http\Response
     *
     * @throws \Prettus\Validator\Exceptions\ValidatorException
     */
    public function store(DormMeterRecordCreateRequest $request)
    {
        try {
            //$this->validator->with($request->all())->passesOrFail(ValidatorInterface::RULE_CREATE);
            set_time_limit(0);
            $dormMeterId = null;
            DB::beginTransaction();
            $prev_reading = 0;
            $mt = $request->get('meter_type_id');
            $meterType = MeterType::find($mt);
            $meterName = $meterType->name;
            $pid = $request->get('period_id');
            $cnt = $this->repository->where('period_id', '>=', $pid)->where('meter_type_id', $mt)->where('dorm_id', $request->get('dorm_id'))->count();
            if ($cnt > 0) {
                $msg = '该宿舍已存在更新周期的[' . $meterName . ']读数，请先删除再添加';
                throw new \Exception($msg);
            }

            $name = 'active' . Str::studly($meterType->code);
            $meter = Dorm::find($request->get('dorm_id'))->$name;
            if ($meter) {
                $prev_reading = $meter->last_reading;
                $dormMeterId = $meter->id;
                $consume = $request->get('meter_reading') - $prev_reading;
                $day_span = Carbon::parse($request->get('meter_reading_date'))->diffInDays($meter->last_reading_date);
                if ($consume >= 0 && $day_span >= 0) {
                    $meter->last_reading = $request->get('meter_reading');
                    $meter->last_reading_date = $request->get('meter_reading_date');
                    $meter->save();
                } else {
                    $msg = '录入的' . $meterName . '读数要大于等于上次记录的读数' . $meter->last_reading . '，并且读表日期要大于等于上次读表日期';
                    throw new \Exception($msg);
                }
            } else {
                $msg = "此宿舍尚未配置关联" . $meterName . "，请到宿舍界面安装" . $meterName;
                throw new \Exception($msg);
            }


            if ($request->has('client_id')) {
                $user_id = Client::query()->find($request->get('client_id'))->user_id;
            } else {
                $user_id = Auth::id();
            }
            $request->merge(['consume' => $consume, 'user_id' => $user_id, 'day_span' => $day_span, 'dorm_meter_id' => $dormMeterId, 'prev_meter_reading' => $prev_reading]);
            $dormMeterRecord = $this->repository->updateOrCreate($request->only(['period_id', 'dorm_meter_id', 'prev_meter_reading']), $request->all());
            DB::commit();

            $response = [
                'message' => '创建成功.',
                'data' => $dormMeterRecord->toArray(),
            ];
            if ($request->wantsJson()) {
                return response()->json($response);
            }
            return redirect()->back()->with('message', $response['message']);
        } catch (\Exception $e) {
            DB::rollBack();
            if ($request->wantsJson()) {
                return response()->json([
                    'error' => true,
                    'message' => $e->getMessage()
                ]);
            }
            \Log::error($e->getMessage());
            return redirect()->back()->withErrors($e->getMessage())->withInput();
        }
    }

    /**
     * Display the specified resource.
     *
     * @param  int $id
     *
     * @return \Illuminate\Http\Response
     */
    public function show($id)
    {
        $dormMeterRecord = $this->repository->find($id);

        if (request()->wantsJson()) {

            return response()->json([
                'data' => $dormMeterRecord,
            ]);
        }

        return view('dormMeterRecords.show', compact('dormMeterRecord'));
    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param  int $id
     *
     * @return \Illuminate\Http\Response
     */
    public function edit($id)
    {
        $dormMeterRecord = $this->repository->with('dorm')->find($id);
        $meterTypes = MeterType::all();
        $periods = Period::where('status', '<', 2)->get();
        return view('dormMeterRecords.edit', compact('dormMeterRecord', 'meterTypes', 'periods'));
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  DormMeterRecordUpdateRequest $request
     * @param  string $id
     *
     * @return Response
     *
     * @throws \Prettus\Validator\Exceptions\ValidatorException
     */
    public function update(DormMeterRecordUpdateRequest $request, $id)
    {
        try {
            $this->validator->with($request->all())->passesOrFail(ValidatorInterface::RULE_UPDATE);
            $recordB4Update = $this->repository->find($id);
            $hasNewerRecord = DormMeterRecord::query()->where('dorm_meter_id', $recordB4Update->dorm_meter_id)->where('period_id', '>', $recordB4Update->period_id)->exists();
            if ($hasNewerRecord) {
                $msg = "存在比当前记录更新的记录，只能更新当前表最新的抄表记录！";
                throw new \Exception($msg);
            }
            //$prev_ts = Carbon::parse($recordB4Update->prev_reading_date)->timestamp;
            $prev_reading = $recordB4Update->prev_meter_reading;

            DB::beginTransaction();
            $day_span = Carbon::parse($request->get('meter_reading_date'))->diffInDays($recordB4Update->prev_reading_date);

            $meter = DormMeter::find($recordB4Update->dorm_meter_id);
            $consume = $request->get('meter_reading') - $prev_reading;


            if ($consume >= 0 && $day_span > 0) {
                $meter->last_reading = $request->get('meter_reading');
                $meter->last_reading_date = $request->get('meter_reading_date');
                $meter->save();
            } else {
                $meterType = $meter->meter_type->name;
                $msg = "录入的 $meterType 读数要大于上次记录的读数 $prev_reading  ，或者读表日期要大于上次读表日期";
                throw new \Exception($msg);
            }

            $request->merge(['consume' => $consume, 'user_id' => Auth::id(), 'day_span' => $day_span]);

            $dormMeterRecord = $this->repository->update($request->all(), $id);

            DB::commit();
            $response = [
                'message' => 'DormMeterRecord updated.',
                'data' => $dormMeterRecord->toArray(),
            ];

            if ($request->wantsJson()) {

                return response()->json($response);
            }

            return redirect()->back()->with('message', $response['message']);
        } catch (\Exception $e) {
            DB::rollBack();
            if ($request->wantsJson()) {
                return response()->json([
                    'error' => true,
                    'message' => $e->getMessage()
                ]);
            }
            return redirect()->back()->withErrors($e->getMessage())->withInput();
        }
    }


    /**
     * Remove the specified resource from storage.
     *
     * @param  int $id
     *
     * @return \Illuminate\Http\Response
     */
    public function destroy($id)
    {
        try {
            DB::beginTransaction();
            $record = $this->repository->find($id);
            $hasNewerRecord = DormMeterRecord::query()->where('dorm_meter_id', $record->dorm_meter_id)->where('period_id', '>', $record->period_id)->exists();
            if ($hasNewerRecord) {
                $msg = "存在比当前记录更新的记录，必须从当前表最新的抄表记录开始删除！";
                throw new \Exception($msg);
            }

            if ($record->period->status == 2) {
                $msg = "所属周期费用已经确认，不能删除";
                throw new \Exception($msg);
            }

            $dormMeter = $record->dormMeter;
            $dormMeter->last_reading = $record->prev_meter_reading;
            $dormMeter->last_reading_date = Carbon::parse($record->meter_reading_date)->subDay($record->day_span);

            $dormMeter->save();

            $deleted = $this->repository->delete($id);

            DB::commit();

            if (request()->wantsJson()) {
                return response()->json([
                    'message' => 'DormMeterRecord deleted.',
                    'deleted' => $deleted,
                ]);
            }

            return redirect()->back()->with('message', 'DormMeterRecord deleted.');
        } catch (\Exception $exception) {
            DB::rollBack();
            if (\request()->wantsJson()) {
                return response()->json([
                    'deleted' => false,
                    'message' => $exception->getMessage()
                ]);
            }

            return redirect()->back()->withErrors($exception->getMessage());
        }
    }

    public function handleExcelImport(Request $request)
    {
        $file = $request->file('file');
        //判断文件是否上传成功
        if ($file->isValid()) {
            try {
                $import = new DormMeterRecordImport();
                Excel::import($import, $request->file('file'));
                return \response()->json(['code' => 0, 'msg' => "共 $import->total 条，成功 $import->success 条，失败 $import->fail 条($import->fail_dorms)，忽略 $import->ignore 条($import->ignore_dorms)"]);
            } catch (\Exception $exception) {
                return \response()->json(['code' => -1, 'msg' => 'fail:' . $exception->getTraceAsString()]);
            }
        } else {
            return \response()->json(['code' => -1, 'msg' => 'file not valid, import fail']);
        }
    }

    public function handleTemplateDownload()
    {
        return response()
            ->download(realpath(base_path('public/template')) . '/dorm_meter_records.xlsx', '抄表导入模板.xlsx');
    }

    //h5抄表记录提交记录
    public function dormMeterRecordings(Request $request)
    {
        $period = Period::find($request->period_id);
        if ($period) {
            $next = $period->next();
            if ($next) {
                //检查下个周期是否已经有抄表记录
                $data = $this->repository->findWhere(['dorm_id' => $request->dorm_id, 'period_id' => $next->id]);
                if (count($data) > 0) {
                    return response()->json(['code' => -1, 'msg' => $next->name . '该宿舍已存在抄表记录，请先删除再导入', 'data' => $data]);
                }
            }
            foreach ($request->readings as $meter_id => $reading) {
                $meter = DormMeter::find($meter_id);
                $record = $this->repository->where(['dorm_id' => $request->dorm_id, 'dorm_meter_id' => $meter->id, 'period_id' => $period->id])->first();
                if ($reading == '') {
                    //如果原来有记录，则删除
                    if ($record) {
                        $meter->last_reading_date = $record->prev_reading_date;
                        $meter->last_reading = $record->prev_meter_reading;
                        $meter->save();
                        $this->repository->delete($record->id);
                    }
                    continue;
                }


                if ($record) {
                    $consume = $reading - $record->prev_meter_reading;
                    if ($consume < 0) {
                        //读数比上一期的小，当作表已经重置
                        $consume = ($meter->reset_limit - $record->prev_meter_reading) + $reading;
                    }
                    $record->update(['user_id' => $request->user_id, 'meter_reading' => $reading, 'consume' => $consume]);
                    // $meter->update(['last_reading' => $reading]);
                } else {
                    $consume = $reading - $meter->last_reading;
                    if ($consume < 0) {
                        //读数比上一期的小，当作表已经重置
                        $consume = ($meter->reset_limit - $meter->last_reading) + $reading;
                    }
                    if ($meter->dorm_id == $request->dorm_id) {
                        //避免重复添加不符合条件的数据
                        $record = $this->repository->create([
                            'user_id' => $request->user_id,
                            'company_id' => $request->company_id,
                            'dorm_id' => $request->dorm_id,
                            'dorm_meter_id' => $meter_id,
                            'meter_type_id' => $meter->meter_type_id,
                            'period_id' => $period->id,
                            'prev_meter_reading' => $meter->last_reading,
                            'prev_reading_date' => $meter->last_reading_date,
                            'meter_reading' => $reading,
                            'consume' => $consume,
                            'meter_reading_date' => date('Y-m-d'),
                            'day_span' => Carbon::now()->diffInDays($meter->last_reading_date),
                        ]);
                    }
                }
                $meter->update(['last_reading' => $reading, 'last_reading_date' => date('Y-m-d')]);
            }
            return response()->json(['code' => 0, 'msg' => 'success']);
        } else {
            return response()->json(['code' => -1, 'msg' => 'period not found']);
        }
    }
}
